home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / dejagnu.lha / dejagnu-1.0.1 / expect / exp_main_sub.c < prev    next >
C/C++ Source or Header  |  1993-04-15  |  16KB  |  636 lines

  1. /* exp_main_sub.c - miscellaneous subroutines for Expect or Tk main() */
  2.  
  3. #include "exp_conf.h"
  4. #include <stdio.h>
  5. #include "tcl.h"
  6. #include "tclInt.h"
  7. #include "exp_rename.h"
  8. #include "exp_main.h"
  9. #include "exp_global.h"
  10. #include "exp_command.h"
  11. #include "exp_tty.h"
  12. #include "exp_log.h"
  13. #include "exp_event.h"
  14. #include "exp_main.h"
  15.  
  16. #ifdef __SABER__
  17. #undef    VERSION
  18. #define    VERSION        "Saber"
  19. #undef    SCRIPTDIR
  20. #define SCRIPTDIR    "test/"
  21. #endif
  22. static char exp_version[] = VERSION;
  23. #define VERSION_VARNAME   "expect_version"
  24. #define NEED_TCL_MAJOR        6
  25. #define NEED_TCL_MINOR        5
  26. #define SCRIPTDIR_VARNAME "expect_library"
  27.  
  28. char *exp_argv0 = "this program";    /* default program name */
  29. void (*exp_app_exit)() = 0;
  30. void (*exp_event_exit)() = 0;
  31. FILE *exp_cmdfile = 0;
  32. int exp_cmdlinecmds = FALSE;
  33. int exp_interactive =  FALSE;
  34. int exp_fgets();
  35.  
  36. static Tcl_Interp *exp_interp;    /* for emergency use only */
  37.  
  38. static void
  39. usage(interp)
  40. Tcl_Interp *interp;
  41. {
  42.     errorlog("usage: expect [-di] [-c cmds] [[-f] cmdfile] [args]\r\n");
  43.     exp_exit(interp,-1);
  44. }
  45.  
  46. void
  47. exp_exit(interp,status)
  48. Tcl_Interp *interp;
  49. int status;
  50. {
  51.     extern int exp_forked;
  52.     static int exp_exit_in_progress = FALSE;
  53.  
  54.     /* prevent recursion */
  55.     if (exp_exit_in_progress) {
  56.         errorlog("exp_exit recursed\r\n");
  57.         exit(status);
  58.     }
  59.     exp_exit_in_progress = TRUE;
  60.  
  61.     /* called user-defined exit routine if one exists */
  62.     exp_generic_sighandler(0);
  63.  
  64.     if (!interp) {
  65.         /* if no interp handy (i.e., called from interrupt handler) */
  66.         /* use last one created - it's a hack but we're exiting */
  67.         /* ungracefully to begin with */
  68.         interp = exp_interp;
  69.     }
  70.     if (exp_app_exit) (*exp_app_exit)(interp);
  71.     if (exp_event_exit) (*exp_event_exit)(interp);
  72.  
  73.     Tcl_DeleteInterp(interp);
  74.  
  75.     if (!exp_disconnected && !exp_forked
  76.         && (dev_tty != -1) && isatty(dev_tty) && ioctled_devtty) {
  77.         tty_set(interp,&exp_tty_original,dev_tty,0);
  78.     }
  79.     /* all other files either don't need to be flushed or will be
  80.        implicitly closed at exit.  Spawned processes are free to continue
  81.        running, however most will shutdown after seeing EOF on stdin.
  82.        Some systems also deliver SIGHUP and other sigs to idle processes
  83.        which will blow them away if not prepared.
  84.     */
  85. #ifdef CRAY
  86.     ttyp_reset();
  87. #endif
  88.     exit(status);
  89. }
  90.  
  91.  
  92. /* this stupidity because Tcl needs commands in writable space */
  93. static char prompt1[] = "prompt1";
  94. static char prompt2[] = "prompt2";
  95.  
  96. static char *prompt2_default = "+> ";
  97. static char prompt1_default[] = "expect%d.%d> ";
  98.  
  99. /*ARGSUSED*/
  100. int
  101. cmdPrompt1(clientData, interp, argc, argv)
  102. ClientData clientData;
  103. Tcl_Interp *interp;
  104. int argc;
  105. char **argv;
  106. {
  107.     Interp *iPtr = (Interp *)interp;
  108.  
  109.     sprintf(interp->result,prompt1_default,
  110.         iPtr->numLevels,iPtr->curEventNum+1);            
  111.     return(TCL_OK);
  112. }
  113.  
  114. /*ARGSUSED*/
  115. int
  116. cmdPrompt2(clientData, interp, argc, argv)
  117. ClientData clientData;
  118. Tcl_Interp *interp;
  119. int argc;
  120. char **argv;
  121. {
  122.     strcpy(interp->result,prompt2_default);
  123.     return(TCL_OK);
  124. }
  125.  
  126. /* user has pressed escape char from interact or somehow requested expect.
  127. If a user-supplied command returns:
  128.  
  129. TCL_ERROR,    assume user is experimenting and reprompt
  130. TCL_OK,        ditto
  131. TCL_RETURN,    return TCL_OK (assume user just wants to escape() to return)
  132. TCL_RETURN_TCL,    return TCL_RETURN
  133. anything else    return it
  134. */
  135. int
  136. exp_interpreter(interp)
  137. Tcl_Interp *interp;
  138. {
  139.     int rc;
  140.     char *ccmd;        /* pointer to complete command */
  141.     char line[BUFSIZ];    /* space for partial command */
  142.     int newcmd = TRUE;
  143.     Tcl_CmdBuf buffer;
  144.     Interp *iPtr = (Interp *)interp;
  145.     int tty_changed = FALSE;
  146.  
  147.     exp_tty tty_old;
  148.     int was_raw, was_echo;
  149.  
  150.     int dummy;
  151.     int fd = fileno(stdin);
  152.  
  153.     expect_key++;
  154.  
  155.     if (!(buffer = Tcl_CreateCmdBuf())) {
  156.         /* why are we doing this, this way rather than, say, */
  157.         /* AppendResult? */
  158.         strcpy(interp->result,"no more space for cmd buffer\r\n");
  159.         return(TCL_ERROR);
  160.     }
  161.     newcmd = TRUE;
  162.     while (TRUE) {
  163.         /* force terminal state */
  164.         tty_changed = tty_cooked_echo(interp,&tty_old,&was_raw,&was_echo);
  165.  
  166.         if (newcmd) {
  167.             rc = Tcl_Eval(interp,prompt1,0,(char **)0);
  168.             if (rc == TCL_OK) Log(1,"%s",interp->result);
  169.             else Log(1,prompt1_default,iPtr->numLevels,
  170.                            iPtr->curEventNum+1);
  171.         } else {
  172.             rc = Tcl_Eval(interp,prompt2,0,(char **)0);
  173.             if (rc == TCL_OK) Log(1,"%s",interp->result);
  174.             else Log(1,prompt2_default,1);
  175.         }
  176.  
  177. #ifdef SHARE_CMD_BUFFER
  178.         if (exp_fgets(interp,line,BUFSIZ) == EXP_EOF) {
  179.             if (!newcmd) line[0] = 0;
  180.             else exp_exit(interp,0);
  181.         }
  182. #else
  183.  
  184.         /* following lines which test for input aren't necessary for */
  185.         /* non-Tk apps, but don't hurt */
  186.         fs[fd].force_read = 1;
  187.         rc = exp_get_next_event(interp,&fd,1,&dummy,EXP_TIME_INFINITY,
  188.             fs[fd].key);
  189.         /*  check for rc == EXP_TCLERROR? */
  190.         
  191.         
  192.         if (rc != EXP_EOF) {
  193.             if (0 >= (rc = read(0,line,BUFSIZ))) {
  194.                 if (!newcmd) line[0] = 0;
  195.                 else rc = EXP_EOF;
  196.             } else line[rc] = '\0';
  197. #if 0
  198.             if (fgets(line,BUFSIZ,stdin) == NULL) {
  199.                 if (!newcmd) line[0] = 0;
  200.                 else rc = EXP_EOF;
  201.             }
  202. #endif
  203.         }
  204.  
  205.         if (rc == EXP_EOF) exp_exit(interp,0);
  206.  
  207.         if (debugfile) fwrite(line,1,strlen(line),debugfile);
  208.         /* intentionally always write to logfile */
  209.         if (logfile) fwrite(line,1,strlen(line),logfile);
  210.         /* no need to write to stdout, since they will see */
  211.         /* it just from it having been echoed as they are */
  212.         /* typing it */
  213. #endif /*SHARE_CMD_BUFFER*/
  214.  
  215.         if (NULL == (ccmd = Tcl_AssembleCmd(buffer,line))) {
  216.             newcmd = FALSE;
  217.             continue;    /* continue collecting command */
  218.         }
  219.         newcmd = TRUE;
  220.  
  221.         if (tty_changed) tty_set(interp,&tty_old,was_raw,was_echo);
  222.         switch (rc = Tcl_RecordAndEval(interp,ccmd,0)) {
  223.         case TCL_OK:
  224.             if (*interp->result != 0)
  225.                 Log(0,"%s\r\n",exp_cook(interp->result,(int *)0));
  226.             continue;
  227.         case TCL_ERROR:
  228.             errorlog("%s\r\n",exp_cook(get_var("errorInfo"),(int *)0));
  229.             /* since user is typing by hand, we expect lots
  230.                of errors, and want to give another chance */
  231.             continue;
  232. #define finish(x)    {rc = x; goto done;}
  233.         case TCL_BREAK:
  234.         case TCL_CONTINUE:
  235.             finish(rc);
  236.         case TCL_RETURN_TCL:
  237.             finish(TCL_RETURN);
  238.         case TCL_RETURN:
  239.             finish(TCL_OK);
  240.         default:
  241.             /* note that ccmd has trailing newline */
  242.             errorlog("error %d: %s\r\n",rc,ccmd);
  243.             continue;
  244.         }
  245.     }
  246.     /* cannot fall thru here, must jump to label */
  247.  done:
  248.     if (tty_changed) tty_set(interp,&tty_old,was_raw,was_echo);
  249.  
  250.     /* currently, code guarantees buffer is valid */
  251.     Tcl_DeleteCmdBuf(buffer);
  252.  
  253.     return(rc);
  254. }
  255.  
  256. /*ARGSUSED*/
  257. int
  258. cmdExpectVersion(clientData, interp, argc, argv)
  259. ClientData clientData;
  260. Tcl_Interp *interp;
  261. int argc;
  262. char **argv;
  263. {
  264.     int emajor, umajor;
  265.     char *user_version;    /* user-supplied version string */
  266.  
  267.     if (argc == 1) {
  268.         Tcl_SetResult(interp,exp_version,TCL_STATIC);
  269.         return(TCL_OK);
  270.     }
  271.     if (argc > 3) {
  272.         exp_error(interp,"usage: expect_version [[-exit] version]");
  273.         return(TCL_ERROR);
  274.     }
  275.  
  276.     user_version = argv[argc==2?1:2];
  277.     emajor = atoi(exp_version);
  278.     umajor = atoi(user_version);
  279.  
  280.     /* first check major numbers */
  281.     if (emajor > umajor) return(TCL_OK);
  282.     else if (emajor == umajor) {
  283.         int u, e;
  284.  
  285.         /* now check minor numbers */
  286.         char *dot = strchr(user_version,'.');
  287.         /* if user did not supply minor number, let it go */
  288.         if (dot) {
  289.             u = atoi(dot+1);
  290.             dot = strchr(exp_version,'.');
  291.             e = atoi(dot+1);
  292.             if (e >= u) return(TCL_OK);
  293.         }
  294.     }
  295.  
  296.     if (argc == 2) {
  297.         exp_error(interp,"%s requires Expect version %s (but using %s)",
  298.             exp_argv0,user_version,exp_version);
  299.         return(TCL_ERROR);
  300.     }
  301.     errorlog("%s: requires Expect version %s (but using %s)\r\n",
  302.         exp_argv0,user_version,exp_version);
  303.     exp_exit(interp,-1);
  304.     /*NOTREACHED*/
  305. }
  306.  
  307. void
  308. exp_init(interp)
  309. Tcl_Interp *interp;
  310. {
  311.     static int first_time = TRUE;
  312.  
  313.     if (first_time) {
  314.         int tcl_major = atoi(TCL_VERSION);
  315.         char *dot = strchr(TCL_VERSION,'.');
  316.         int tcl_minor = atoi(dot+1);
  317.  
  318.         if (tcl_major < NEED_TCL_MAJOR || tcl_minor < NEED_TCL_MINOR) {
  319.             fprintf(stderr,
  320.                "%s compiled with Tcl %d.%d but needs at least Tcl %d.%d\n",
  321.                 exp_argv0,tcl_major,tcl_minor,
  322.                 NEED_TCL_MAJOR,NEED_TCL_MINOR);
  323.                exit(-1);
  324.         }
  325.  
  326.         exp_init_pty();
  327.         exp_init_tty(); /* do this only now that we have looked at */
  328.                 /* original tty state */
  329.         exp_init_sig();
  330.         exp_init_event();
  331.         exp_init_trap();
  332.         exp_init_unit_random();
  333.         exp_init_spawn_ids(interp);
  334.  
  335.         first_time = FALSE;
  336.     }
  337.  
  338.     /* save last known interp for emergencies */
  339.     exp_interp = interp;
  340.  
  341.     /* call explicitly, so user can put history info in init prompt */
  342.     Tcl_InitHistory(interp);
  343.  
  344.     /* initialize commands */
  345.     exp_create_commands(interp);/* add non-expect cmds to interpreter */
  346.     exp_init_expect(interp);    /* add expect cmds to interpreter */
  347.     exp_init_spawn_id_vars(interp);
  348.  
  349.     Tcl_SetVar(interp,SCRIPTDIR_VARNAME,SCRIPTDIR,0);
  350.     Tcl_SetVar(interp,VERSION_VARNAME,VERSION,0);
  351. }
  352.  
  353. void
  354. exp_parse_argv(interp,argc,argv)
  355. Tcl_Interp *interp;
  356. int argc;
  357. char **argv;
  358. {
  359.     static char *cmdfilename = 0;
  360.  
  361.     int sys_rc = TRUE;    /* read system rc file */
  362.     int my_rc = TRUE;    /* read personal rc file */
  363.  
  364.     int c;
  365.     int rc;
  366.  
  367.     extern int optind;
  368.     extern char *optarg;
  369.     char *args;        /* ptr to string-rep of all args */
  370.  
  371.     exp_argv0 = argv[0];
  372.  
  373.     while ((c = getopt(argc, argv, "c:dDf:inN")) != EOF) {
  374.         switch(c) {
  375.         case 'c': /* command */
  376.             exp_cmdlinecmds = TRUE;
  377.             rc = Tcl_Eval(interp,optarg,0,(char **)0);
  378.  
  379.             if (rc != TCL_OK) {
  380.                 errorlog("%s\r\n",exp_cook(get_var("errorInfo"),(int *)0));
  381.             }
  382.             break;
  383.         case 'd': exp_is_debugging = TRUE;
  384.             debuglog("expect version %s\r\n",exp_version);
  385.             break;
  386. #ifdef EXP_DEBUGGER
  387.         case 'D':
  388.             exp_init_debugger(interp,argc,argv,exp_interpreter);
  389.             break;
  390. #endif
  391.         case 'f': /* name of cmd file */
  392.             cmdfilename = optarg;
  393.             break;
  394.         case 'i': /* interactive */
  395.             exp_interactive = TRUE;
  396.             break;
  397.         case 'n': /* don't read personal rc file */
  398.             my_rc = FALSE;
  399.             break;
  400.         case 'N': /* don't read system-wide rc file */
  401.             sys_rc = FALSE;
  402.             break;
  403.         default: usage(interp);
  404.         }
  405.     }
  406.  
  407.     for (c = 0;c<argc;c++) {
  408.         debuglog("argv[%d] = %s  ",c,argv[c]);
  409.     }
  410.     debuglog("\r\n");
  411.  
  412.     /* if user hasn't explicitly requested we be interactive */
  413.     /* look for a file or some other source of commands */
  414.     if (!exp_interactive) {
  415.     /* get cmd file name, if we haven't got it already */
  416.     if (!cmdfilename && (optind < argc)) {
  417.         cmdfilename = argv[optind];
  418.         optind++;
  419.     }
  420.  
  421.     if (cmdfilename) {
  422.         if (streq(cmdfilename,"-")) {
  423.             exp_cmdfile = stdin;
  424.         } else if (NULL == (exp_cmdfile = fopen(cmdfilename,"r"))) {
  425.             errorlog("%s: %s\r\n",cmdfilename,sys_errlist[errno]);
  426.             exp_exit(interp,-1);
  427.         }
  428.         } else if (!exp_cmdlinecmds) {
  429.             /* no other source of commands, force interactive */
  430.             exp_interactive = TRUE;
  431.         }
  432.     }
  433.  
  434. #if 0
  435.     if (exp_cmdfile && exp_interactive) {
  436.         errorlog("cannot read commands from both file and keyboard\r\n");
  437.         exp_exit(interp,-1);
  438.     }
  439. #endif
  440.  
  441.     /* collect remaining args and make into an argv */
  442.     /* Tcl expects argv[0] to be the command name, but it doesn't do */
  443.     /* anything useful with it anyway, so just back up the pointer */
  444.     /* actually, back it up twice, so we can make the resultant [0] */
  445.     /* be the program name */
  446.     /* Oh, and we add to argc for the same reason */
  447.     /* If no cmdfilename, there certainly can't be any args! */
  448.     if (cmdfilename) {
  449.         argv[optind-1] = cmdfilename;
  450.     } else  {
  451.         argv[optind-1] = exp_argv0;
  452.     }
  453.  
  454.         Tcl_ListCmd((ClientData)0,interp,2+argc-optind,argv+optind-2);
  455.         args = interp->result;
  456. /*    } else args = "";*/
  457.  
  458.     debuglog("set argv \"%s\"\r\n",args);
  459.     Tcl_SetVar(interp,"argv",args,0);
  460.  
  461.     exp_interpret_rcfiles(interp,my_rc,sys_rc);
  462. }
  463.  
  464. /* read rc files */
  465. void
  466. exp_interpret_rcfiles(interp,my_rc,sys_rc)
  467. Tcl_Interp *interp;
  468. int my_rc;
  469. int sys_rc;
  470. {
  471.     int rc;
  472.  
  473.     if (sys_rc) {
  474.         char file[200];
  475.         int fd;
  476.  
  477.         sprintf(file,"%sexpect.rc",SCRIPTDIR);
  478.         if (-1 != (fd = open(file,0))) {
  479.         if (TCL_ERROR == (rc = Tcl_EvalFile(interp,file))) {
  480.             errorlog("error executing system initialization file: %s\r\n",file);
  481.             if (rc != TCL_ERROR)
  482.                 errorlog("Tcl_Eval = %d\r\n",rc);
  483.             if (*interp->result != 0)
  484.                 errorlog("%s\r\n",interp->result);
  485.             exp_exit(interp,-1);
  486.         }
  487.         close(fd);
  488.         }
  489.     }
  490.     if (my_rc) {
  491.         char file[200];
  492.         char *home;
  493.         int fd;
  494.         char *getenv();
  495.  
  496.         if (NULL != (home = getenv("HOME"))) {
  497.         sprintf(file,"%s/.expect.rc",home);
  498.         if (-1 != (fd = open(file,0))) {
  499.             if (TCL_ERROR == (rc = Tcl_EvalFile(interp,file))) {
  500.             errorlog("error executing file: %s\r\n",file);
  501.             if (rc != TCL_ERROR)
  502.                 errorlog("Tcl_Eval = %d\r\n",rc);
  503.             if (*interp->result != 0)
  504.                 errorlog("%s\r\n",interp->result);
  505.             exp_exit(interp,-1);
  506.             }
  507.             close(fd);
  508.             }
  509.         }
  510.     }
  511. }
  512.  
  513. void
  514. exp_interpret_cmdfile(interp,fp)
  515. Tcl_Interp *interp;
  516. FILE *fp;
  517. {
  518.     int rc;
  519.  
  520.     Tcl_CmdBuf buffer;
  521.     int newcmd;
  522.  
  523.     debuglog("executing commands from command file\r\n");
  524.  
  525.     if (!(buffer = Tcl_CreateCmdBuf())) {
  526.         errorlog("no more space for cmd buffer\r\n");
  527.         exp_exit(interp,0);
  528.     }
  529.     newcmd = TRUE;
  530.     while (1) {
  531.         char line[BUFSIZ];/* buffer for partial Tcl command */
  532.         char *ccmd;    /* pointer to complete Tcl command */
  533.  
  534.         if (fgets(line,BUFSIZ,fp) == NULL) {
  535.             if (newcmd) break;
  536.             else line[0] = 0;
  537.         }
  538.         if (NULL == (ccmd = Tcl_AssembleCmd(buffer,line))) {
  539.             newcmd = FALSE;
  540.             continue;    /* continue collecting command */
  541.         }
  542.         newcmd = TRUE;
  543.  
  544.         rc = Tcl_Eval(interp,ccmd,0,(char **)0);
  545.  
  546.         if (rc != TCL_OK) {
  547.             /* no \n at end, since ccmd will already have one. */
  548.             /* Actually, this is not true if command is last in */
  549.             /* file and has no newline after it, oh well */
  550.             errorlog("%s\r\n",exp_cook(get_var("errorInfo"),(int *)0));
  551.         }
  552.     }
  553.     /*NOTREACHED*/
  554. }
  555.  
  556. #ifdef SHARE_CMD_BUFFER
  557. /* fgets that shared input buffer with expect_user */
  558. int
  559. exp_fgets(interp,buf,max)
  560. Tcl_Interp *interp;
  561. char *buf;
  562. int max;
  563. {
  564.     char *nl;    /* position of newline which signifies end of line */
  565.     int write_count;/* length of first line of incoming data */
  566.  
  567.     int m = fileno(stdin);
  568.     struct f *f;
  569.     int cc;
  570.  
  571.     int dummy;
  572.  
  573.     /* avoid returning no data, just because someone else read it in by */
  574.     /* passing most recent key */
  575.     cc = exp_get_next_event(interp,&m,1,&dummy,EXP_TIME_INFINITY,fs[m].key);
  576.  
  577.     if (cc == EXP_DATA_NEW) {
  578.         /* try to read it */
  579.  
  580.         cc = i_read(m,EXP_TIME_INFINITY);
  581.  
  582.         /* the meaning of 0 from i_read means eof.  Muck with it a */
  583.         /* little, so that from now on it means "no new data arrived */
  584.         /* but it should be looked at again anyway". */
  585.         if (cc == 0) {
  586.             cc = EXP_EOF;
  587.         } else if (cc > 0) {
  588.             f = fs + m;
  589.             f->buffer[f->size += cc] = '\0';
  590.         }
  591.     } else if (cc == EXP_DATA_OLD) {
  592.         f = fs + m;
  593.         cc = 0;
  594.     }
  595.  
  596.     /* EOF and TIMEOUT return here */
  597.     /* In such cases, there is no need to update screen since, if there */
  598.     /* was prior data read, it would have been sent to the screen when */
  599.     /* it was read. */
  600.     if (cc < 0) return (cc);
  601.  
  602.     /* copy up to end of first line */
  603.  
  604.     /* calculate end of first line */
  605.     nl = strchr(f->buffer,'\n');
  606.     if (nl) write_count = 1+nl-f->buffer;
  607.     else write_count = f->size;
  608.  
  609.     /* make sure line fits in buffer area */
  610.     if (write_count > max) write_count = max;
  611.  
  612.     /* copy it */
  613.     memcpy(buf,f->buffer,write_count);
  614.     buf[write_count] = '\0';
  615.  
  616.     /* update display and f */
  617.  
  618.     f->printed = 0;
  619.     /* for simplicity force f->printed = 0.  This way, the user gets */
  620.     /* to see the commands that are about to be executed.  Not seeing */
  621.     /* commands you are supposedly typing sounds very uncomfortable! */
  622.  
  623.     if (logfile_all || (loguser && logfile)) {
  624.         fwrite(f->buffer,1,write_count,logfile);
  625.     }
  626.     if (debugfile) fwrite(f->buffer,1,write_count,debugfile);
  627.  
  628.     f->size -= write_count;
  629.     memcpy(f->buffer,f->buffer+write_count,1+f->size);
  630.     /* copy to lowercase buffer */
  631.     exp_lowmemcpy(f->lower,f->buffer,1+f->size);
  632.  
  633.     return(write_count);
  634. }
  635. #endif /*SHARE_CMD_BUFFER*/
  636.